package cdbft
/*
cdbft：共识节点被划分为多个组织，每个组织有一个代表节点，每个组织共识后的交易要再被代表节点中共识，经过两次共识后，最后结果才是最终结果
为防止各组织中存在冲突交易，这里默认每次只有一个组织可以参加共识
 */

import (
	"blockchain/public/entity"
	"fmt"
)
const N=entity.N //节点数
const BlockSize=entity.BlockSize
const TradeSize=entity.TradeSize
const clientSeq=499
var currentRound=0
var currentTradeSeq=0
var View=0
var trades [10000]entity.Trade
var node [N] entity.Node
var primarySeq=0
var replicasSeq [N-1]int
var initialNode=false
//var TradeSet [N][TradeSize] entity.Trade
var tradeSet[N][TradeSize]entity.Trade
var chanPriRecRequest=make([]chan requestMessage,N)
var chanRecPre_prepare=make([] chan pre_prepareMessage,N)
var blockchain[200] entity.Block
var chanRecPrepare=make([]chan prepareMessage,N)
var FlagPrepared=make([][]chan bool,N)
var chanRecCommit=make([]chan commitMessage,N)
var FlagCommitted=make([][]chan bool,N)
var chanRecReply=make(chan replyMessage,2*N/3+1)
var FlagReplied=make([]chan bool,N)
var chanFinish=make(chan bool,1)
var blockChain[600] block
var sumrounds=entity.SumRounds
const groupNum=entity.G
var delegate[groupNum] int
var groupNodeNum =N/groupNum
var currentRequest requestMessage

func Main(){
	count :=0
	for i:=0;i<N;i++{
		if i%groupNodeNum==0{
			delegate[count]=i
			count++
		}
	}
	fmt.Printf("CDBFT共识:N=%d,R=%d",N,sumrounds)
	for currentRound=0;currentRound<sumrounds;currentRound++{
		//fmt.Printf("第%d轮共识\n",currentRound)
		request()
		chanRecPre_prepareInitial()
		chanRecPrepareInitial()
		chanRecCommitInitial()
		flagInitial()
		chanFinish=make(chan bool,1)
		chanRecReply=make(chan replyMessage,2*N/3+1)
		pre_prepare(primarySeq)
		for i:=0;i<N/groupNum;i++{
			go nodeConsensus(i)
		}
		for i:=0;i<N/groupNum;i++{
			<-FlagReplied[i]
		}


		chanRecPre_prepareInitial()
		chanRecPrepareInitial2()
		chanRecCommitInitial2()
		flagInitial()


		pre_prepareDelegate(primarySeq)
		for i:=0;i<groupNum;i++{
			go delagateConsensus(delegate[i])
		}
		for i:=0;i<groupNum;i++{
			<-FlagReplied[delegate[i]]
		}
		close(chanRecReply)
		finish()
		<-chanFinish
	}
}

func flagInitial(){
	for nodeSeq:=0;nodeSeq<N;nodeSeq++{
		FlagPrepared[nodeSeq]=make([]chan bool,N)
		for j:=0;j<N;j++{
			FlagPrepared[nodeSeq][j]=make(chan bool,1)
		}
		FlagCommitted[nodeSeq]=make([]chan bool,N)
		for j:=0;j<N;j++{
			FlagCommitted[nodeSeq][j]=make(chan bool,1)
		}
		FlagReplied[nodeSeq]=make(chan bool,1)
	}
}

func nodeConsensus(nodeSeq int){
	prepare(nodeSeq)
	for j:=0;j<N/groupNum;j++{
		<-FlagPrepared[j][nodeSeq]
	}
	close(chanRecPrepare[nodeSeq])
	commit(nodeSeq)
	for j:=0;j<N/groupNum;j++{
		<-FlagCommitted[j][nodeSeq]
	}
	close(chanRecCommit[nodeSeq])
	reply(nodeSeq)
}


func delagateConsensus(nodeSeq int){
	prepareDelegate(nodeSeq)
	for j:=0;j<groupNum;j++{
		<-FlagPrepared[delegate[j]][nodeSeq]
	}
	close(chanRecPrepare[nodeSeq])
	commitDelegate(nodeSeq)
	for j:=0;j<groupNum;j++{
		<-FlagCommitted[delegate[j]][nodeSeq]
	}
	close(chanRecCommit[nodeSeq])
	replyDelegate(nodeSeq)
}

func chanRecPre_prepareInitial(){
	for i:=0;i<N;i++{
		chanRecPre_prepare[i]=make(chan pre_prepareMessage,1)
	}
}

func chanRecPrepareInitial(){
	for i:=0;i<N;i++{
		chanRecPrepare[i]=make(chan prepareMessage,2*(N/groupNum)/3+1)
	}
}

func chanRecCommitInitial(){
	for i:=0;i<N;i++{
		chanRecCommit[i]=make(chan commitMessage,2*(N/groupNum)/3+1)
	}
}

func chanRecPrepareInitial2(){
	for i:=0;i<N;i++{
		chanRecPrepare[i]=make(chan prepareMessage,2*(N/groupNodeNum)/3+1)
	}
}

func chanRecCommitInitial2(){
	for i:=0;i<N;i++{
		chanRecCommit[i]=make(chan commitMessage,2*(N/groupNodeNum)/3+1)
	}
}

func updatePrimarys(){
	if node[primarySeq].ByzantineTurnNum==0{

	}else{
		primarySeq=currentRound%N
		View++
	}
}